01 - Agent 的 Trace 长什么样
需要了解分布式追踪的基本概念(trace 是一次请求的完整记录,span 是其中的一段操作)。不需要用过任何具体的可观测平台。
本篇回答:Agent 的一次执行应该被拆成哪些 span、有哪些指标是传统监控里没有的、最少埋哪几个点就能用。
本篇会用到的词:
| 词 | 意思 |
|---|---|
gen_ai.operation.name | OpenTelemetry 用来区分 span 类型的那个属性。它的取值(chat / execute_tool / invoke_agent 等)就是下面要讲的十种操作类型 |
| 根 span | 一棵 trace 最外层的那个 span,代表「一次完整的用户请求」。它 的耗时等于用户实际等待的时间 |
| TTFT | Time To First Token,首字延迟 —— 从发出请求到吐出第一个 token 的时间,这段时间用户屏幕是空的 |
| TPOT | Time Per Output Token,也叫 ITL —— 吐出后续每个 token 之间的间隔,决定「吐字」快不快 |
| prefill / decode | 模型推理的两个阶段:prefill 一次性处理完整个输入(决定 TTFT),decode 之后逐个吐 token(决定 TPOT) |
| PII | Personally Identifiable Information,个人可识别信息。姓名、手机号、身份证号这类,落进 trace 之前要先脱敏 |
一、一次执行展开成一棵树
用户输入:"帮我查一下我们上周的销售数据,做个总结。"
一次用户请求产生六个 span:三次模型调用、两次工具调用、一次检索。
1.1 与传统监控的差别
| 传统 APM | Agent trace | |
|---|---|---|
| 记录内容 | POST /chat 耗时 18.4s | 六个 span 的树结构 |
| 能回答 | 慢 | 慢在哪一跳 |
| 调用次数 | 代码写死,固定 | 每次执行都可能不同 |
| 成本 | 与调用次数无关 | 与 token 用量直接相关 |
上图中最后一个 span 占了 18.4 秒里的 9.2 秒。没有这棵树,"慢"这个结论无法再往下分解一层。
二、十种操作类型
GenAI 语义约定用 gen_ai.operation.name 区分 span 类型,共十个取值:
| 取值 | 含义 | 产生时机 |
|---|---|---|
create_agent | 创建 Agent | 初始化 |
invoke_agent | 调用 Agent | 树根,或子 Agent 调用 |
invoke_workflow | 调用工作流 | 编排层 |
execute_tool | 执行工具 | 每次工具调用 |
retrieval | 检索 | RAG 召回环节 |
plan | 规划 | Plan-and-Execute 类范式 |
chat | 对话补全 | 每次模型调用 |
text_completion | 文本补全 | 旧式补全接口 |
generate_content | 生成内容 | 多模态 |
embeddings | 向量化 | 计算 embedding |
2.1 这份清单反映的建模视角
invoke_agent、plan、execute_tool 这三个取值的存在,说明规范的心智模型是 Agent 的生命周期,而不是 LLM API 的调用。
对比 2024 年的版本 —— 当时只有 chat 和 embeddings,因为那时的典型用法只是调用模型。取值集合的扩张,直接记录了这两年应用形态的变化。
三、九个指标
比 span 更适合做告警的是指标。GenAI 约定下的 gen_ai.* 指标:
# 客户端侧:单次模型调用的性能与用量
gen_ai.client.operation.duration # 整体耗时
gen_ai.client.operation.time_to_first_chunk # 首字延迟,即 TTFT
gen_ai.client.operation.time_per_output_chunk # 每 token 间隔,即 TPOT
gen_ai.client.token.usage # token 用量
# Agent 侧:一次 Agent 执行的行为特征
gen_ai.invoke_agent.duration # Agent 整体耗时
gen_ai.invoke_agent.inference_calls # 本次执行调用了几次模型
gen_ai.invoke_agent.tool_calls # 本次执行调用了几个工具
gen_ai.invoke_workflow.duration # 工作流耗时
gen_ai.execute_tool.duration # 单个工具耗时
3.1 耗时被拆成三个独立指标
operation.duration、time_to_first_chunk、time_per_output_chunk —— 规范用三个指标表达"快慢",而不是一个。
原因在于三者的关系:
只记总耗时,则输出长度不同的两次请求无法比较 —— 一次输出 100 token 用 3 秒和一次输出 2000 token 用 20 秒,后者其实更快。
这与 Agent 网关 · Token 速率与 QoS 从工程实践得出的结论一致:time_to_first_chunk 即 TTFT,time_per_output_chunk 即 TPOT。
3.2 两个传统监控里没有的指标
gen_ai.invoke_agent.inference_calls
gen_ai.invoke_agent.tool_calls
传统服务的下游调用次数由代码写死,因此"调了几次"不是一个需要监控的量。Agent 不同:同一个问题,模型今天可能两步解决,明天可能绕十步。
这两个指标是发现"模型开始绕远路"的唯一手段:
| 观察到的现象 | 可能原因 |
|---|---|
inference_calls 的 P99 翻倍 | 换了模型版本,或改了提示词导致模型反复试探 |
tool_calls 均值上升而 inference_calls 不变 | 工具返回质量下降,模型需要多次调用才能拿到可用结果 |
| 两者都上升、成本上升、成功率不变 | 净损失,应当告警 |
成本失控通常不是单价上涨,而是步数增加。 步 数只有这两个指标能观测到。
四、MCP 使用同一套词汇
规范仓库中有一份 docs/gen-ai/mcp.md(1,332 行):MCP 的工具调用与发起它的 Agent 共用同一套 trace 词汇。
这意味着一棵 trace 树可以同时覆盖 Agent 编排层和 MCP 工具层,不需要在两套体系间做转换 —— 对 Agent 网关 · MCP 网关描述的工具聚合场景尤其重要,因为网关本身就处在两层之间。
五、规范尚未稳定
截至 2026-08,没有任何一个 gen_ai.* 属性、span 或指标被标记为 Stable。
规范中标记为稳定的 128 个属性,全部是从核心语义约定继承的通用项(server.address、error.type 这类),与 GenAI 无关。
此外它在 2026 年发生过一次仓库迁移:v1.42.0(2026-06-12)将全部 gen_ai.* 从主仓库拆出,成立独立的 semantic-conventions-genai 仓库(创建于 2026-05-05)。
5.1 应对方式
不稳定不等于不能用,但需要隔离变更影响:
# ❌ 直接在业务代码里写属性名
# 规范一改,所有调用点都要跟着改
span.set_attribute("gen_ai.usage.input_tokens", n)
# ✅ 收敛到一个薄封装层
# 规范变更时只改这一处;也便于同时输出两套约定(见 02 篇)
class AgentSpan:
"""对埋点属性的唯一出口。业务代码不直接接触属性名。"""
def set_input_tokens(self, n: int) -> None:
# 规范未稳定,属性名可能变化 —— 变更只影响这一行
self._span.set_attribute("gen_ai.usage.input_tokens", n)
具体的选型策略见 02 篇。
六、最小可用埋点清单
从零开始时,下面这组覆盖大部分排查与计费需求:
| 优先级 | 埋什么 | 解决什么问题 |
|---|---|---|
| 1 | invoke_agent 根 span,含 inference_calls / tool_calls | 发现步数异常 |
| 2 | 每次 chat span,含 token 用量 | 成本归因的原料 |
| 3 | time_to_first_chunk 与 time_per_output_chunk |